/*
 * 	Flan.Zeng 2011-2016	http://git.oschina.net/signup?inviter=flan
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.flan.stock.utils;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Collections;
import java.util.Map;
import java.util.WeakHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Color;
import android.os.Handler;
import android.widget.ImageView;

/**
 * 异步图片加载工具类
 * 使用文件缓存时，需要用到SD卡读写权限
 * @author flan
 * @date   2015年11月12日
 */
public class AsyncImageLoader {
	
	private MemoryCache memoryCache;
	private FileCache fileCache;
	private ExecutorService executorService;
	private Handler handler = new Handler();//handler to display images in UI thread
	private Map<ImageView, String> imageViews = Collections.synchronizedMap(new WeakHashMap<ImageView, String>());
	
	/**
	 * @param context	android 上下文对象
	 * @param threads	线程池中启动的线程数
	 */
	public AsyncImageLoader(Context context,int threads) {
		super();
		
		this.memoryCache = new MemoryCache();
		this.fileCache = new FileCache(context);
		this.executorService = Executors.newFixedThreadPool(threads);
	}
	
	/**
	 * 显示网络图片
	 * @param imgUrl
	 * @param imageView
	 */
	public void DisplayImage(String imgUrl,ImageView imageView){
		
		imageViews.put(imageView, imgUrl);
		
		// 先从内存缓存中查找
		Bitmap bitmap = memoryCache.getBitmap(imgUrl);
		if(bitmap != null){
			imageView.setImageBitmap(bitmap);
		}else {
			//内存中没有图片，则开启线程加载图片
			queueImage(imgUrl,imageView);
			imageView.setBackgroundColor(Color.WHITE);	//先设置为默认图片
		}
		
	}
	
	private void queueImage(String imgUrl,ImageView imageView){
		ImageLoadTask task = new ImageLoadTask(imgUrl, imageView);
		executorService.submit(new ImageLoader(task));
	}
	
	/**
	 * 线程队列中的Task
	 * @author flan
	 * @date   2015年11月12日
	 */
	private class ImageLoadTask{
		
		public String imageUrl;
		public ImageView imageView;
		
		public ImageLoadTask(String imageUrl, ImageView imageView) {
			super();
			this.imageUrl = imageUrl;
			this.imageView = imageView;
		}
		
	}
	
	class ImageLoader implements Runnable{
		
		private ImageLoadTask task;
		
		public ImageLoader(ImageLoadTask task) {
			super();
			this.task = task;
		}

		@Override
		public void run() {
			
			if(imageViewReused(task)){
				return;
			}
			
			Bitmap bitmap = getBitmap(task.imageUrl);
			memoryCache.put(task.imageUrl, bitmap);
			
			if(imageViewReused(task)){
				return;
			}
			BitmapDisplayer bd = new BitmapDisplayer(bitmap,task);
			handler.post(bd);
		}
	}
	
	/**
	 * 防止图片错位
	 * @param task
	 * @return
	 */
	private boolean imageViewReused(ImageLoadTask task){
		String tag = imageViews.get(task.imageView);
		if(tag!=null && !tag.equals(task.imageUrl)){
			return true;
		}
		return false;
	}
	
	private Bitmap getBitmap(String imgUrl){
		
		//先从文件缓存中查找是否有相应的图片，如果有则返回
		File file = fileCache.getFile(imgUrl);
		Bitmap fileBitmap = decodeFile(file);
		if(fileBitmap != null){
			return fileBitmap;
		}
		
		try {
			Bitmap bitmap=null;
			URL imageUrl = new URL(imgUrl);
			HttpURLConnection conn = (HttpURLConnection)imageUrl.openConnection();
			conn.setConnectTimeout(30000);
			conn.setReadTimeout(30000);
			conn.setInstanceFollowRedirects(true);
			InputStream is=conn.getInputStream();
			OutputStream os = new FileOutputStream(file);
			
			int buffer_size=1024;
			byte[] bytes=new byte[buffer_size];
			for(;;){
			  int count=is.read(bytes, 0, buffer_size);
			  if(count==-1)
			      break;
			  os.write(bytes, 0, count);
			}
			
			os.close();
			conn.disconnect();
			bitmap = decodeFile(file);
			return bitmap;
		} catch (MalformedURLException e) {
			e.printStackTrace();
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return null;
	}
	
	/**
	 * decode这个图片并且按比例缩放以减少内存消耗，虚拟机对每张图片的缓存大小也是有限制的
	 * @param file
	 * @return
	 */
	private Bitmap decodeFile(File file){
		
		try {
			//decode image size
			BitmapFactory.Options opts = new BitmapFactory.Options();
			opts.inJustDecodeBounds = true;
			FileInputStream fis = new FileInputStream(file);
			BitmapFactory.decodeStream(fis, null, opts);
			fis.close();
			
			final int REQUIRED_SIZE = 70;
			int width_tmp = opts.outWidth, height_tmp = opts.outHeight;
			int scale = 1;
			while(true){
			    if(width_tmp/2<REQUIRED_SIZE || height_tmp/2<REQUIRED_SIZE)
			        break;
			    width_tmp/=2;
			    height_tmp/=2;
			    scale*=2;
			}
			
			//decode with inSampleSize
			BitmapFactory.Options o2 = new BitmapFactory.Options();
			o2.inSampleSize=scale;
			FileInputStream stream2=new FileInputStream(file);
			Bitmap bitmap=BitmapFactory.decodeStream(stream2, null, o2);
			stream2.close();
			return bitmap;
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return null;
	}
	
	/**
	 * 用于在UI线程中更新界面
	 * @author flan
	 * @date   2015年11月12日
	 */
	private class BitmapDisplayer implements Runnable{
		
		private Bitmap bitmap;
		private ImageLoadTask task;
		
		public BitmapDisplayer(Bitmap bitmap, ImageLoadTask task) {
			super();
			this.bitmap = bitmap;
			this.task = task;
		}

		@Override
		public void run() {
			if(imageViewReused(task)){
				return;
			}
			
			if(bitmap != null){
				task.imageView.setImageBitmap(bitmap);
			}else {
				task.imageView.setBackgroundColor(Color.WHITE);
			}
		}
	}
	
	/**
	 * 同时清除 内存缓存和文件缓存
	 */
	public void clearCache(){
		memoryCache.clear();
		fileCache.clear();
	}
	/**
	 * 清理内存缓存
	 */
	public void clearMemoryCache(){
		memoryCache.clear();
	}
	/**
	 * 清理文件缓存
	 */
	public void clearFileCache(){
		fileCache.clear();
	}
}


